#ifdef __KERNEL__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/version.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/thermal.h>
#include <asm/neon.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/pm_clock.h>
#include <linux/cpufreq.h>
#include <linux/notifier.h>
#include <mach/efuse.h>
#else
#include <rtthread.h>
#include "irq_numbers.h"
#include "clk.h"
#endif
#include "ei_os.h"

typedef struct eiAXNU_DEV_S {
    void __iomem *base;
    int irq;
    unsigned int freq;
} AXNU_DEV_S;

AXNU_DEV_S *pstAXNU;

int s32AxnuIrqRegistered = 0;
int gax_power_on_flg = 0;
int gax_prcm_clk_en = 0;
int g_clk_on = 0;
int limited_freq_by_volt = 0;

extern unsigned int fixed_clock;
extern unsigned int limited_freq;
static unsigned int special_freq;
extern unsigned int setting_freq;

#ifdef __KERNEL__
EXPORT_SYMBOL_GPL(gax_power_on_flg);
EXPORT_SYMBOL_GPL(gax_prcm_clk_en);
#endif

os_wait_t stAxnuWait;
os_wait_t stAxnuWait2;
os_wait_t stAxnuWait_freq_vol;
extern os_mutex_t stAxnuMutex;
unsigned long pcrmclampaddress;
#ifdef __KERNEL__
static struct clk *pstAxnuAhbGate;
static struct clk *pstAxnuAhbReset;
static struct clk *pstAxnuAxiGate;
static struct clk *pstAxnuClamp;
static struct device * pstDev;
#else
static clk_handle_t pstAxnuAhbGate;
static clk_handle_t pstAxnuAhbReset;
static clk_handle_t pstAxnuAxiGate;
static clk_handle_t pstAxnuClamp;
#endif

extern int AXNU_ModInit(void);
extern int AXNU_ModExit(void);
extern int AXNU_DisableIrq(int dbg_flag);
extern void AXNU_PM_Open(int line_num);
extern void AXNU_PM_Close(int line_num);

extern void AXNU_SetAdpllEnable(unsigned int enable,unsigned int base_addr);
extern void AXNU_SetInternalGating(unsigned int nvx_core_mask, unsigned int enable, unsigned int base_addr);
extern void AXNU_CheckSetOcmPowerEnable(unsigned int enable, unsigned int base_addr);
extern long AXNU_wait_idle(void);
extern int AXNU_adjust_freq_by_volt(void);
extern int NCORE_ENABLE_MASK;

#ifdef __KERNEL__
extern unsigned int AXNU_SetCoreInit(unsigned int base_addr);
//extern int AXNU_SetAdpllFreeRunAcc(unsigned int freq_hz, unsigned int base_addr);
//extern unsigned int AXNU_SetAdpll(unsigned int freq, unsigned int base_addr);
extern int lombo_memctrl_bw_ctrl_mode(const char *master, unsigned int ctrl_mode, int rw);
extern int lombo_memctrl_bw_randomode(const char *master, unsigned int randomode, int rw);
extern int lombo_memctrl_bw_seed(const char *master, unsigned int seed, int rw);
extern int lombo_memctrl_bw_timewin(const char *master, unsigned int timewin, int rw);
extern int lombo_memctrl_bw_datasize(const char *master, unsigned int datasize, int rw);
#endif

long TimeAfter(unsigned long long a, unsigned long long b)
{
#ifdef __KERNEL__
    return time_after((unsigned long)a, (unsigned long)b);
#else
    return (a>=b) ? 1 : 0;
#endif
}

void ei_axnu_fp_enable(void)
{
#ifdef __KERNEL__
    kernel_neon_begin();
#endif
}

void ei_axnu_fp_disable(void)
{
#ifdef __KERNEL__
    kernel_neon_end();
#endif
}

void ei_axnu_common_gate_enable(void)
{
#ifdef __KERNEL__
#ifdef CONFIG_PM_GENERIC_DOMAINS
    pm_runtime_get_sync(pstDev);
#else
    clk_prepare_enable(pstAxnuAhbGate);
    clk_prepare_enable(pstAxnuAxiGate);
#endif
#else
    clk_enable(pstAxnuAhbGate);
    clk_enable(pstAxnuAxiGate);
#endif
    
}

void ei_axnu_common_gate_disable(void)
{
#ifdef __KERNEL__
#ifdef CONFIG_PM_GENERIC_DOMAINS    
    pm_runtime_put_sync(pstDev);
#else
    clk_disable_unprepare(pstAxnuAhbGate);
    clk_disable_unprepare(pstAxnuAxiGate);
#endif
#else
    clk_disable(pstAxnuAhbGate);
    clk_disable(pstAxnuAxiGate);
#endif
}

void ei_axnu_clk_prepare_enable(void)
    {
#ifdef __KERNEL__
    clk_prepare_enable(pstAxnuAhbGate);
    clk_prepare_enable(pstAxnuAhbReset);
    clk_prepare_enable(pstAxnuAxiGate);
    clk_prepare_enable(pstAxnuClamp);
#else
    clk_enable(pstAxnuAhbGate);
    clk_enable(pstAxnuAhbReset);
    clk_enable(pstAxnuAxiGate);
    clk_enable(pstAxnuClamp);
#endif
}

void ei_axnu_ahb_clk_enable(void)
{
#ifdef __KERNEL__
    clk_prepare_enable(pstAxnuAhbReset);
#else
    clk_enable(pstAxnuAhbReset);
#endif
}

void ei_axnu_ahb_clk_disable(void)
{
#ifdef __KERNEL__
    clk_disable_unprepare(pstAxnuAhbReset);
#else
    clk_disable(pstAxnuAhbReset);
#endif
}

void ei_axnu_clk_disable_unprepare(void)
{
#ifdef __KERNEL__
    clk_disable_unprepare(pstAxnuClamp);
    clk_disable_unprepare(pstAxnuAhbGate);
    clk_disable_unprepare(pstAxnuAhbReset);
    clk_disable_unprepare(pstAxnuAxiGate);
 #else
    clk_disable(pstAxnuClamp);
    clk_disable(pstAxnuAhbGate);
    clk_disable(pstAxnuAhbReset);
    clk_disable(pstAxnuAxiGate);
 #endif
}


#ifdef CONFIG_PM_GENERIC_DOMAINS
static void AXNU_Adpll_Enable(void)
{
    AXNU_SetAdpllEnable(1, (unsigned int)pstAXNU->base);
    AXNU_SetInternalGating(NCORE_ENABLE_MASK, 1, (unsigned int)pstAXNU->base);
}

static void AXNU_Adpll_Disable(void)
{
    AXNU_CheckSetOcmPowerEnable(0, (unsigned int)pstAXNU->base);
    AXNU_SetInternalGating(0, 0, (unsigned int)pstAXNU->base);
    AXNU_SetAdpllEnable(0, (unsigned int)pstAXNU->base);
}
#endif

void axnu_pm_clk_enable(void)
{
    ei_axnu_common_gate_enable();
#ifndef CONFIG_PM_GENERIC_DOMAINS
    AXNU_SetAdpllEnable(1, (unsigned int)pstAXNU->base);
    AXNU_SetInternalGating(NCORE_ENABLE_MASK, 1, (unsigned int)pstAXNU->base);
#endif
}

void axnu_pm_clk_disable(void)
{
#ifndef CONFIG_PM_GENERIC_DOMAINS
    AXNU_CheckSetOcmPowerEnable(0, (unsigned int)pstAXNU->base);
    AXNU_SetInternalGating(0, 0, (unsigned int)pstAXNU->base);
    AXNU_SetAdpllEnable(0, (unsigned int)pstAXNU->base);
#endif
    ei_axnu_common_gate_disable();
}


#ifdef __KERNEL__
static irqreturn_t ei_axnu_ISR(int irq, void *dev_id)
#else
static int ei_axnu_ISR(int irq, void *dev_id)
#endif
{    
    int valid_flg = AXNU_DisableIrq(0);
    if ((valid_flg)) {
        os_wakeup(&stAxnuWait2);
        os_wakeup(&stAxnuWait);
#ifdef CONFIG_LOMBO_CPU_FREQ_AVS
        os_wakeup(&stAxnuWait_freq_vol);
#endif
    }
#ifdef __KERNEL__
    return IRQ_HANDLED;
#else
    return OSAL_IRQ_HANDLED;
#endif
}

#ifdef __KERNEL__
struct nu_cooling_device {
    struct thermal_cooling_device *cdev;
    unsigned int nu_cool_state;
    unsigned int target_cool_state;
};
static struct nu_cooling_device *nu_dev = NULL;

static unsigned int freq_tab[4] = { 999, 600, 500, 400 };
static unsigned int band_width_limit[2][4] = { {182536, 58411, 29205, 21904},  {182536, 43808, 29205, 21904}};

static int ei_axnu_freq_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
{
    if (!state)
        return -EINVAL;
    *state = 3;
    return 0;
}

static int ei_axnu_freq_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
{
    //struct nu_cooling_device *nu_dev = cdev->devdata;
    *state = nu_dev->nu_cool_state;   
    return 0;
}

static int ei_axnu_freq_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
    //struct nu_cooling_device *nu_dev = cdev->devdata;

    nu_dev->target_cool_state = state;
    return 0;
}

int axnu_freq_set_cur_state(void)
{
    int freq = 0;
    int reset_freq = 0;
    int type;
    unsigned int bandwidth;
    unsigned int state;

    //struct nu_cooling_device *nu_dev = cdev->devdata;

    state = nu_dev->target_cool_state;

    if (state >= sizeof(freq_tab) / sizeof(unsigned int))
        return -EINVAL;

    if (nu_dev->nu_cool_state == state)
        return 0;

    nu_dev->nu_cool_state = state;
    
    freq = freq_tab[state];
    reset_freq = 0;

    if (setting_freq > freq_tab[state]) {
        freq = freq_tab[state];
    }
    else {
        freq = setting_freq;
    }
    if (pstAXNU->freq != freq) {
        reset_freq = 1;
    }

#if 0  // DCDC with CPU, Using fixed adpll config. Disable freq change when cool_state change
    if (reset_freq == 1) {
        os_mutex_lock(&stAxnuMutex);
        AXNU_PM_Open(__LINE__);
        if (!special_freq) {
            pstAXNU->freq = AXNU_SetAdpllFreeRunAcc(freq, pstAXNU->base);
        }
        else {
            pstAXNU->freq = AXNU_SetAdpll(freq, pstAXNU->base);
        }
        AXNU_PM_Close(__LINE__);
        os_mutex_unlock(&stAxnuMutex);
    }
#endif

    //bandwidth

    
    if ((os_readl(0xF1210000)>>12) & 0x3) {
        bandwidth = ((os_readl(0xF400A000 + 0x4A4) >>8 ) & 0xff) * 33;	
    }
    else {
        bandwidth = ((os_readl(0xF400A000 + 0x4A4) >>8 ) & 0xff) * 67;		
    }
    if (bandwidth > 3000) {
        type = 0;
    }
    else {
        type = 1;
    }

    if (lombo_memctrl_bw_ctrl_mode("AX0", 3, 0) != 0 ||
        lombo_memctrl_bw_randomode("AX0", 2, 0) != 0 ||
        lombo_memctrl_bw_seed("AX0", 0, 0) != 0 ||
        lombo_memctrl_bw_timewin("AX0", 6800, 0) != 0 ||
        lombo_memctrl_bw_datasize("AX0", band_width_limit[type][state], 0) != 0 ||
        lombo_memctrl_bw_ctrl_mode("AX0", 3, 1) != 0 ||
        lombo_memctrl_bw_randomode("AX0", 2, 1) != 0 ||
        lombo_memctrl_bw_seed("AX0", 0, 1) != 0 ||
        lombo_memctrl_bw_timewin("AX0", 6800, 1) != 0 ||
        lombo_memctrl_bw_datasize("AX0", band_width_limit[type][state], 1) != 0)
    {
        os_printk("AXNU bandwidth limit error.\n");
        return -EINVAL;
    }
    return 0;
}

static struct thermal_cooling_device_ops const nufreq_cooling_ops = {
    .get_max_state = ei_axnu_freq_get_max_state,
    .get_cur_state = ei_axnu_freq_get_cur_state,
    .set_cur_state = ei_axnu_freq_set_cur_state,
};

static struct thermal_cooling_device * ei_axnu_freq_cooling_register(struct device_node *np)
{
    struct thermal_cooling_device *cool_dev = NULL;
    //struct nu_cooling_device *nu_dev = NULL;
    char dev_name[THERMAL_NAME_LENGTH];

    nu_dev = os_kzalloc(sizeof(struct nu_cooling_device), os_gfp_kernel);
    if (!nu_dev)
        return ERR_PTR(-ENOMEM);

    snprintf(dev_name, sizeof(dev_name), "axnu-thermal");
    cool_dev = thermal_of_cooling_device_register(np, dev_name, nu_dev, &nufreq_cooling_ops);
    if (IS_ERR(cool_dev)) {
        os_printk("register cooling device error: %s\n", dev_name);
        return cool_dev;
    }
    nu_dev->cdev = cool_dev;
    nu_dev->nu_cool_state = 0;

    special_freq = 0;
    setting_freq = pstAXNU->freq;  

    return cool_dev;
}

#if 0
#ifdef CONFIG_LOMBO_CPU_FREQ_AVS
static int ei_axnu_volt_charge_notifier(struct notifier_block *notifier,
		unsigned long voltage, void *v)
{
	struct lombo_cpufreq *l_cpufreq = (struct lombo_cpufreq *)v;
	long left_time;

	os_mutex_lock(&stAxnuMutex);
	if (voltage <= 850)
		limited_freq_by_volt = 1;
	else
		limited_freq_by_volt = 0;

	left_time = AXNU_wait_idle();
	if (left_time <= 0) {
		os_printk("stAxnuWait_freq_vol timeout\n");
	}

	AXNU_adjust_freq_by_volt();
	os_mutex_unlock(&stAxnuMutex);
	complete(&l_cpufreq->volt_completion);
	return NOTIFY_OK;
}
static struct notifier_block axnu_charge_notifier = {
	.notifier_call = ei_axnu_volt_charge_notifier,
	.priority = INT_MIN,
};
#endif
#endif

#else
int axnu_freq_set_cur_state(void)
{
    return 0;
}
#endif

#ifdef __KERNEL__
#ifdef CONFIG_LOMBO_CPU_FREQ_AVS
extern unsigned int axnu_ready_flag;
#endif
static int ei_axnu_probe(struct platform_device *pdev)
{
    AXNU_DEV_S *nu;
    struct resource *iores;
    int ret = 0;
    int fixed_clock_default_flag = 0;
    
    pstDev = &pdev->dev;

    nu = devm_kzalloc(&pdev->dev, sizeof(AXNU_DEV_S), GFP_KERNEL);
    if (!nu) {
        ret = -ENOMEM;
        goto devm_kzalloc_err;
    }

    iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!iores) {
        ret = -EINVAL;
        goto platform_get_resource_err;
    }

    nu->base = devm_ioremap_resource(&pdev->dev, iores);
    if (IS_ERR(nu->base)) {
        ret = PTR_ERR(nu->base);
        goto devm_ioremap_resource_err;
    }
    
    nu->irq = platform_get_irq(pdev, 0);
    if (nu->irq < 0) {
        ret = nu->irq;
        goto get_irq_err;
    }
    
    ret = request_irq(nu->irq, (void *)ei_axnu_ISR, 0, "ei_axnu", 0);
    if (ret) {
        s32AxnuIrqRegistered = 0;
        ret = -1;
        goto request_irq_err;
    } else {
        s32AxnuIrqRegistered = 1;
    }

    pstAXNU = nu;
    if (gax_power_on_flg == 0) {
        pstAxnuAhbGate = devm_clk_get(&pdev->dev, "ax_ahb_gate");
        if (IS_ERR(pstAxnuAhbGate)) {
            ret = PTR_ERR(pstAxnuAhbGate);
            goto devm_clk_get_nu_ahb_gate_err;
        }
        pstAxnuAhbReset = devm_clk_get(&pdev->dev, "ax_ahb_reset");
        if (IS_ERR(pstAxnuAhbReset)) {
            ret = PTR_ERR(pstAxnuAhbReset);
            goto devm_clk_get_nu_ahb_reset_err;
        }
        pstAxnuAxiGate = devm_clk_get(&pdev->dev, "ax_axi_gate");
        if (IS_ERR(pstAxnuAxiGate)) {
            ret = PTR_ERR(pstAxnuAxiGate);
            goto devm_clk_get_nu_axi_gate_err;
        }                
        pstAxnuClamp = clk_get(NULL, "ax_clamp_gate");

        clk_prepare_enable(pstAxnuAhbReset);
    #ifdef CONFIG_PM_GENERIC_DOMAINS
        ret = pm_clk_add_clk(&pdev->dev, pstAxnuAhbGate);
        ret = pm_clk_add_clk(&pdev->dev, pstAxnuAxiGate);
        ret = pm_clk_add_clk(&pdev->dev, pstAxnuClamp);
	    ret |= pm_clk_resume(&pdev->dev);
	    pm_runtime_set_active(&pdev->dev);
	    pm_runtime_enable(&pdev->dev);        
    #else
        clk_prepare_enable(pstAxnuAhbGate);
        clk_prepare_enable(pstAxnuAxiGate);
        clk_prepare_enable(pstAxnuClamp);
    #endif
        pcrmclampaddress = (unsigned long)ioremap(0x0400A020, 0x4);

    #ifndef CONFIG_PM_GENERIC_DOMAINS
        g_clk_on ++;
    #endif
        ei_axnu_fp_enable();

        if (of_property_read_u32(pdev->dev.of_node, "fixed_clock", &fixed_clock)) {
            fixed_clock = 0;
        }
        if (of_property_read_u32(pdev->dev.of_node, "limited_clock", &limited_freq)) {
            limited_freq = 0;
        }

        if (of_property_read_u32(pdev->dev.of_node, "fixed_clock_default", &fixed_clock_default_flag)) {
            fixed_clock_default_flag = 0;
        }
        if (fixed_clock_default_flag > 0) {
            unsigned char *buf_tmp = NULL;
            int id = 0x61;
            int module_len;
            ret = lombo_get_efuse_module_len(id, &module_len);
            if (module_len > 0) {
                buf_tmp = os_kzalloc(module_len, os_gfp_kernel);
                if (buf_tmp != NULL) {
                    ret = lombo_efuse_read(id, (void *)buf_tmp, module_len);
                    if (ret != 0) {
                        os_printk("axnu get efuse eror\n");
                    } else {
                        if ((*buf_tmp == 0x0) || (*buf_tmp == 0x3)){
                            fixed_clock = 600;
                        } else if (*buf_tmp == 0x1) {
                            fixed_clock = 472;
                        } else if (*buf_tmp == 0x2) {
                            fixed_clock = 284;
                        } else {
                            ;
                        }
                    }
                    os_kfree(buf_tmp);
                } else {
                    os_printk("axnu malloc tmp buffer eror\n");
                }
            } else {
                os_printk("axnu get module len error : %d\n", module_len);
            }
        }

        pstAXNU->freq = AXNU_SetCoreInit((unsigned int)pstAXNU->base);
        ei_axnu_fp_disable();

        if (lombo_memctrl_bw_ctrl_mode("AX0", 3, 0) != 0 ||
            lombo_memctrl_bw_randomode("AX0", 2, 0) != 0 ||
            lombo_memctrl_bw_seed("AX0", 0, 0) != 0 ||
            lombo_memctrl_bw_timewin("AX0", 6800, 0) != 0 ||
            lombo_memctrl_bw_datasize("AX0", band_width_limit[0][0], 0) != 0 ||
            lombo_memctrl_bw_ctrl_mode("AX0", 3, 1) != 0 ||
            lombo_memctrl_bw_randomode("AX0", 2, 1) != 0 ||
            lombo_memctrl_bw_seed("AX0", 0, 1) != 0 ||
            lombo_memctrl_bw_timewin("AX0", 6800, 1) != 0 ||
            lombo_memctrl_bw_datasize("AX0", band_width_limit[0][0], 1) != 0) {
            os_printk("AXNU bandwidth limit init error.\n");
        } else {
            ;//os_printk("AXNU bandwidth limit init ok.\n");
        }


    #ifndef CONFIG_PM_GENERIC_DOMAINS
        AXNU_PM_Close(__LINE__);
    #endif
    }
    gax_power_on_flg++;
    gax_prcm_clk_en = 0;

    os_wait_init(&stAxnuWait);
    os_wait_init(&stAxnuWait2);
#ifdef CONFIG_LOMBO_CPU_FREQ_AVS
    os_wait_init(&stAxnuWait_freq_vol);
#endif
    AXNU_ModInit();
    ei_axnu_freq_cooling_register(pdev->dev.of_node);
#ifdef CONFIG_LOMBO_CPU_FREQ_AVS
    //register_volt_charge_notifier(&axnu_charge_notifier);
	//axnu_ready_flag = 1;
#endif

    return 0;

    clk_put(pstAxnuClamp);
devm_clk_get_nu_axi_gate_err:
    devm_clk_put(&pdev->dev, pstAxnuAhbReset);
devm_clk_get_nu_ahb_reset_err:
    devm_clk_put(&pdev->dev, pstAxnuAhbGate);
devm_clk_get_nu_ahb_gate_err:
    free_irq(nu->irq, 0);
request_irq_err:
get_irq_err:
platform_get_resource_err:
devm_ioremap_resource_err:
    devm_kfree(&pdev->dev, nu);
devm_kzalloc_err:
    return ret;
}

static int ei_axnu_remove(struct platform_device *pdev)
{
    gax_power_on_flg--;
    if (gax_power_on_flg == 0) {
    #ifdef CONFIG_PM_GENERIC_DOMAINS
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        pm_clk_remove_clk(&pdev->dev, pstAxnuAxiGate);
        pm_clk_remove_clk(&pdev->dev, pstAxnuAhbGate);
        pm_clk_remove_clk(&pdev->dev, pstAxnuClamp);
    #else
        devm_clk_put(&pdev->dev, pstAxnuAxiGate);
        devm_clk_put(&pdev->dev, pstAxnuAhbGate);
        clk_put(pstAxnuClamp);
    #endif
        devm_clk_put(&pdev->dev, pstAxnuAhbReset);
    }

    if (s32AxnuIrqRegistered == 1) {
        free_irq(pstAXNU->irq, 0);
    }

    if (pstAXNU != NULL) {
        devm_kfree(&pdev->dev, pstAXNU);
        pstAXNU = NULL;
    }

    pstDev = NULL;

    os_wait_destroy(&stAxnuWait);
    os_wait_destroy(&stAxnuWait2);
#ifdef CONFIG_LOMBO_CPU_FREQ_AVS
    os_wait_destroy(&stAxnuWait_freq_vol);
#endif
    AXNU_ModExit();

    return 0;
}

static const struct of_device_id ei_axnu_match[] = {
    { .compatible = "ei, ei-axnu" },
    {},
};
MODULE_DEVICE_TABLE(of, ei_axnu_match);

#ifdef CONFIG_PM_GENERIC_DOMAINS
static int lombo_axnu_runtime_suspend(struct device *dev)
{
    AXNU_Adpll_Disable();
    return 0;
}

static int lombo_axnu_runtime_resume(struct device *dev)
{
    AXNU_Adpll_Enable();
    return 0;
}

static int lombo_axnu_suspend_noirq(struct device *dev)
{
    if (!pm_runtime_status_suspended(dev)) {
        AXNU_Adpll_Disable();
    }
    return 0;
}

static int lombo_axnu_resume_noirq(struct device *dev)
{
    if (!pm_runtime_status_suspended(dev)) {
        AXNU_Adpll_Enable();
    }
    return 0;
}
#endif

#ifdef CONFIG_PM_GENERIC_DOMAINS
static int lombo_axnu_suspend(struct device *dev)
{
	return 0;
}

static int lombo_axnu_resume(struct device *dev)
{
    clk_disable_unprepare(pstAxnuAhbReset);
    clk_prepare_enable(pstAxnuAhbReset);

    if (pm_runtime_status_suspended(dev)) {
        pm_clk_resume(dev);
        AXNU_Adpll_Enable();
    }

    ei_axnu_fp_enable();
    AXNU_SetCoreInit((unsigned int)pstAXNU->base);
    ei_axnu_fp_disable();

    if (pm_runtime_status_suspended(dev)) {
        pm_clk_suspend(dev);
        AXNU_Adpll_Disable();
    }

    return 0;
}
#endif

#ifdef CONFIG_PM
static const struct dev_pm_ops lombo_axnu_pm_ops = {
#ifdef CONFIG_PM_GENERIC_DOMAINS
	SET_RUNTIME_PM_OPS(lombo_axnu_runtime_suspend, lombo_axnu_runtime_resume, NULL)
	.suspend_noirq = lombo_axnu_suspend_noirq,
	.resume_noirq = lombo_axnu_resume_noirq,
#endif

#ifdef CONFIG_PM_GENERIC_DOMAINS
	SET_SYSTEM_SLEEP_PM_OPS(lombo_axnu_suspend, lombo_axnu_resume)
#endif
};
#endif

static struct platform_driver ei_axnu_driver = {
    .probe          = ei_axnu_probe,
    .remove         = ei_axnu_remove,
    .driver         =
    {
        .name           = "ei_axnu",
        .of_match_table = ei_axnu_match,
#ifdef CONFIG_PM
		.pm = &lombo_axnu_pm_ops,
#endif
    },
};
os_module_platform_driver(ei_axnu_driver);

MODULE_LICENSE("GPL");
#else
int axnu_driver_init()
{
    AXNU_DEV_S *nu;
    int ret = 0;
    unsigned long res_phy_addr;
    unsigned long res_vir_addr;
    unsigned int res_size;

    nu = os_kzalloc(sizeof(AXNU_DEV_S), os_gfp_kernel);
    if (!nu) {
        ret = -ENOMEM;
        os_printk("malloc axnu dev buffer error\n");
        goto dev_malloc_err;
    }

    res_phy_addr = 0x01700000;
    res_size = 100000;
    res_vir_addr = (unsigned long)os_ioremap_nocache(res_phy_addr, res_size);
    nu->base = (void *)res_vir_addr;

    nu->irq  = INT_AX_NVX;
    ret = os_request_irq(nu->irq, ei_axnu_ISR, 0, "AXNU_CODEC_IRQ", (void *)nu);
    if (ret) {
        s32AxnuIrqRegistered = 0;
        os_printk("init irq error\n");
    } else {
        s32AxnuIrqRegistered = 1;
    }

    pstAXNU = nu;
    if (gax_power_on_flg == 0) {
        pstAxnuAhbGate = clk_get("ahb_ax_gate");
        if (!pstAxnuAhbGate) {
            os_printk("failed to get ahb gate clk\n");
            goto ahb_ax_gate_get_err;
        }
        pstAxnuAhbReset = clk_get("ahb_ax_reset");
        if (!pstAxnuAhbReset) {
            os_printk("failed to get ahb ax reset clk\n");
            goto ahb_ax_reset_get_err;
        }
        pstAxnuAxiGate = clk_get("maxi_ax_gate");
        if (!pstAxnuAxiGate) {
            os_printk("failed to get maxi ax gate\n");
            goto maxi_ax_gate_get_err;
        }

        clk_enable(pstAxnuAhbGate);
        clk_enable(pstAxnuAhbReset);
        clk_enable(pstAxnuAxiGate);

        pstAxnuClamp = clk_get("ax_clamp_gate");
        if (!pstAxnuClamp) {
            os_printk("failed to get ax clamp gate\n");
            goto ax_clamp_gate_get_err;
        }
        clk_enable(pstAxnuClamp);

        pcrmclampaddress = (unsigned long)os_ioremap_nocache(0x0400A020, 0x4);

        g_clk_on ++;
        ei_axnu_fp_enable();
		fixed_clock = 0;
		limited_freq = 0;
        pstAXNU->freq = AXNU_SetCoreInit((unsigned int)pstAXNU->base);
        ei_axnu_fp_disable();
        AXNU_PM_Close(__LINE__);
    }

    gax_power_on_flg++;
    gax_prcm_clk_en = 0;  

    os_wait_init(&stAxnuWait);
    os_wait_init(&stAxnuWait2);
    AXNU_ModInit();
    return 0;

ax_clamp_gate_get_err:
    clk_put(pstAxnuAxiGate);
maxi_ax_gate_get_err:
    clk_put(pstAxnuAhbReset);
ahb_ax_reset_get_err:
    clk_put(pstAxnuAhbGate);
ahb_ax_gate_get_err:
    os_kfree(nu);
dev_malloc_err:
    return ret;
}
#endif
